
goog.provide('jsmeta.grammar');
goog.require('jsmeta.parser');

function derive(parent) {
  function N() {}
  N.prototype = parent;
  return new N();
}

function Grammar(name) {
  this.name = name;
  this.rules = {};
  this.combinators = derive(Grammar.combinators);
}

Grammar.prototype.addRule = function(name, pattern) {
  if (!this.top)
    this.top = name;
  this.rules[name] = pattern;
};

Grammar.prototype.parse = function(inputString) {
  var ctx = new ParseContext(inputString);
  ctx.grammar = this;
  var p = this.rules[this.top].clone();
  return p.run(ctx);
};

Grammar.prototype.isRulePure = function(ruleName) {
  this.purityRules = {};
  this.purityRules[ruleName] = true;
  return this.rules[ruleName].isPure(this);
};

Grammar.prototype.isRuleRecursive = function(ruleName) {
  this.recursionRules = {};
  this.recursionRules[ruleName] = true;
  return this.rules[ruleName].isRecursive(this);
};

function alternate_applicator() {
  var n = this instanceof Number ? this : 0;
  if (n < arguments.length - 2) {
    return new Parser_Either(arguments[n], alternate_applicator.apply(n+1, arguments));
  }
  if (arguments.length == 1)
    return arguments[0];
  return new Parser_Either(arguments[arguments.length - 2], arguments[arguments.length - 1]);
}

function sequence_applicator() {
  var n = this instanceof Number ? this : 0;
  if (n < arguments.length - 2) {
    return new Parser_Both(arguments[n], sequence_applicator.apply(n+1, arguments));
  }
  if (arguments.length == 1)
    return arguments[0];
  return new Parser_Both(arguments[arguments.length - 2], arguments[arguments.length - 1]);
}

Grammar.combinators = {
  'plus' : function(p) {
    return new Parser_Repeat(p, 1, -1)
  },
  'star' : function(p) {
    return new Parser_Repeat(p, 0, -1)
  },
  'both' : function(l, r) {
    return new Parser_Both(l, r);
  },
  'either' : function(l, r) {
    return new Parser_Either(l, r);
  },
  'opt' : function(p) {
    return new Parser_Either(p, new Parser_Empty());
  },
  'empty' : function() {
    return new Parser_Empty();
  },
  'lit' : function(text) {
    return new Parser_Literal(text);
  },
  'final' : function(p) {
    return new Parser_Final(p);
  },
  'looka' : function(p) {
    return new Parser_Lookahead(p);
  },
  'lookb' : function(p) {
    return new Parser_Lookbehind(p);
  },
  'not' : function(p) {
    return new Parser_Not(p);
  },
  'rep' : function(p, min, max) {
    return new Parser_Repeat(p, min, max);
  },
  'end' : function() {
    return new Parser_End();
  },
  'cutl' : function(p, level) {
    return new Parser_Cutlevel(p, level);
  },
  'cutp' : function(p, level) {
    return new Parser_Cutpoint(p, level);
  },
  'call' : function(ruleName) {
    return new Parser_RuleCall(ruleName);
  },
  'callback' : function(closure, contextClosure, argsClosure) {
    return new Parser_Callback(closure, contextClosure, argsClosure);
  },
  'node' : function(p, props) {
    return new Parser_Node(p, props);
  },
  'action' : function(pattern, closure, contextClosure, argsClosure) {
    return new Parser_Both(pattern, new Parser_Callback(closure, contextClosure, argsClosure));
  },
  'alt' : alternate_applicator,
  'seq' : sequence_applicator
};






